
class Handler:
    def call(self, event):
        raise NotImplementedError()


class TimeoutHandler(Handler):
    def call(self, event):
        if isinstance(event, TimeoutEvent):
            return self.timeout(event)
        else:
            raise ValueError("event type not TimeoutEvent")
            
    def timeout(self, event):
        raise NotImplementedError()

    def __str__(self):
        return "TimeoutHandler"


class ReadableHandler(Handler):
    def call(self, event):
        if isinstance(event, ReadableEvent):
            return self.read(event)
        else:
            raise ValueError("event type not ReadableEvent")
            

    def read(self, event):
        raise NotImplementedError()

    def __str__(self):
        return "ReadableHandler"


class WritableHandler(Handler):
    def call(self, event):
        if isinstance(event, WritableEvent):
            return self.write(event)
        else:
            raise ValueError("event type not WritableEvent")

    def write(self, event):
        raise NotImplementedError()

    def __str__(self):
        return "WritableHandler"
    
class SimpleHandler:
    class Timeout(TimeoutHandler):
        def __init__(self, callback=None, *args):
            self.__callback = callback
            self.__args = args
        def timeout(self, event):
            if self.__callback is not None:
                self.__callback(event, *self.__args)
    class Readable(ReadableHandler):
        def __init__(self, callback=None, *args):
            self.__callback = callback
            self.__args = args
        def read(self, event):
            if self.__callback is not None:
                self.__callback(event, *self.__args)

    class Writable(WritableHandler):
        def __init__(self, callback=None, *args):
            self.__callback = callback
            self.__args = args
        def write(self, event):
            if self.__callback is not None:
                self.__callback(event, *self.__args)



import threading  

class IdentityGenerator:
    def __init__(self):
        self.__lock = threading.Lock()
        self.__cur = 0

    def next(self):
        with self.__lock:
            self.__cur += 1
            return self.__cur

    def current(self):
        with self.__lock:
            return self.__cur





from enum import Enum
class Event:
    @staticmethod 
    def is_timer(event):
        return Event.Type.Timer == event.type()
    @staticmethod 
    def is_readable(event):
        return Event.Type.Readable == event.type()
    @staticmethod 
    def is_writable(event):
        return Event.Type.Writable == event.type()
    @staticmethod
    def is_file_events(event):
        return event.type() in (Event.Type.Readable, Event.Type.Writable)

    class Type(Enum):
        Readable = 0x01
        Writable = 0x02
        Timer = 0x04

        @staticmethod
        def valid(tp):
            return tp in (Event.Type.Readable, Event.Type.Writable, Event.Type.Timer)


    def __init__(self, identity, etype, handler, persistent):
        self.__etype = etype
        self.__id = identity
        self.__handler = handler
        self.__persistent = persistent

    def id(self):
        return self.__id
    def set_id(self, identity):
        if self.__id != identity: self.__id = identity
        return self

    def type(self):
        return self.__etype
    def set_type(self, etype):
        if self.__etype != etype:
            self.__etype = etype
        return self

    def handler(self):
        return self.__handler
    def set_handler(self, handler):
        if self.__handler != handler:
            self.__handler = handler
        return self

    def persistent(self):
        return self.__persistent
    def set_persistent(self, persistent):
        if self.__persistent != persistent:
            self.__persistent = persistent
        return self

    def call_handler(self):
        if self.__handler is not None:
            return self.__handler.call(self)
        return None

    def __str__(self):
        return "id:{}, type:{}, persistent:{}".format(self.id(), self.type(), self.persistent())


class TimeoutEvent(Event):
    def __init__(self, identity, timeout_ms, timeout_handler, persistent):
        super().__init__(identity, Event.Type.Timer, timeout_handler, persistent)
        self.__timespan  = timeout_ms
        self.__timeout = 0
        self.__is_deleted = False

    #readonly for get timeout(ms)
    def timespan(self):
        return self.__timespan

    #for internal use (ns) , will cmp with now
    def timeout(self):
        return self.__timeout
    def set_timeout_with_now(self, now):
        timeout = now + self.__timespan * 1000000
        if self.__timeout != timeout:
            self.__timeout = timeout
        return self

    def is_deleted(self):
        return self.__is_deleted
    def delete(self):
        self.__is_deleted = False
        return self

    #will move to internal use
    def __lt__(self, other):
        return self.__timeout < other.__timeout
    def __le__(self, other):
        return self.__timeout <= other.__timeout
    def __eq__(self, other):
        return self.__timeout == other.__timeout
    def __gt__(self, other):
        return self.__timeout > other.__timeout
    def __ge__(self, other):
        return self.__timeout >= other.__timeout

    def __str__(self):
        return super().__str__() + ", timespan:{}, timeout:{}, is_deleted:{}".format(self.__timespan, self.__timeout, self.__is_deleted)


class FileEvent(Event):
    def __init__(self, identity, fno, etype, handler, persistent):
        super().__init__(identity, etype, handler, persistent)
        self.__fno = fno

    def fileno(self):
        return self.__fno
        
    def __str__(self):
        return super().__str__() + ", fileno:[{}]".format(self.__fno.fileno())

class TransportEvent(FileEvent):
    def __init__(self, identity, transport, etype, handler, persistent):
        super().__init__(identity, transport.handle(), etype, handler, persistent)
        self.__transport = transport

    def transport(self):
        return self.__transport

    def __str__(self):
        return super().__str__() + ", transport:[{}]".format(self.__transport)


class ReadableEvent(FileEvent):
    def __init__(self, identity, fno, readable_handler, persistent):
        super().__init__(identity, fno, Event.Type.Readable, readable_handler, persistent)
        
    def __str__(self):
        return super().__str__()


class WritableEvent(FileEvent):
    def __init__(self, identity, fno, writable_handler, persistent):
        super().__init__(identity, fno, Event.Type.Writable, writable_handler, persistent)

    def __str__(self):
        return super().__str__()




import unittest


class TestEvents(unittest.TestCase):

    def test_event_id_generator(self):
        gen = IdentityGenerator()
        print(gen.current())
        print(gen.next())



    class MyTimeoutHandler(TimeoutHandler):
        def timeout(self, event):
            return event.id()

    def test_timeout(self):
        identity = 1
        timeout_ms = 100
        e = TimeoutEvent(identity, timeout_ms, TestEvents.MyTimeoutHandler(), True)
        self.assertTrue(identity == e.id() and Event.Type.Timer == e.type() and e.persistent() == True)
        self.assertEqual(identity, e.call_handler())



    class MyReadableHandler(ReadableHandler):
        def read(self, event):
            return event.id()

    def test_readable(self):
        identity = 2
        fileno = 13
        e = ReadableEvent(identity, fileno, TestEvents.MyReadableHandler(), True)
        self.assertTrue(identity == e.id() and Event.Type.Readable == e.type() and e.persistent() == True)
        self.assertEqual(fileno , e.fileno())

        self.assertEqual(identity, e.call_handler())



    class MyWritableHandler(WritableHandler):
        def write(self, event):
            return event.id()

    def test_writable(self):
        identity = 3
        fileno = 34
        e = WritableEvent(3, fileno, TestEvents.MyWritableHandler(), False)
        self.assertTrue(identity == e.id() and Event.Type.Writable == e.type() and e.persistent() == False)
        self.assertEqual(fileno , e.fileno())
        
        self.assertEqual(identity, e.call_handler())

        
class TestSimpleHandler(unittest.TestCase):
    def test_main(self):
        pass


if __name__ == "__main__":
    unittest.main(verbosity=2)
